#pragma once
#include <dinfra/list.h>
#include <ddblk/apds/combine_int.h>
#include <dbg.h>

#define CACFLAT_CLEANUP_FREQUENCY 		(8 << 1UL)
#define CACFLAT_CLEANUP_OLD_THRESHOLD 		(0 << 1UL)
/* 分别flat 和全局 flat */
/* 全局状态 */

/* 这个flatnode分配怎么处理? */
/* flat申请的时候会附带 flatnode? 而且可扩展 */

#define CACFLAT_GET_AGE(cur_age_active)		((cur_age_active) & (~0x1UL))
#define CACFLAT_INCR_AGE(cur_age_active)	((cur_age_active) + 0x2UL)
#define CACFLAT_PENDING				(0x1UL)
#define CACFLAT_ACTIVE				(0x1UL)
#define CACFLAT_IS_ACTIVE(age_active)		((age_active) & 0x1UL)
#define CACFLAT_GET_AGE_ACTIVE(age_active_flag)	((hword_t)(age_active_flag))
#define CACFLAT_GET_FLAG(age_active_flag)	((age_active_flag) >> HWORD_WIDTH)

typedef struct _cacflatnode
{
	struct{
		APDSFUTURE_DECL_NOFLAG
		/* 整个就是一个word, 写入的时候可以一起写入(同时更新age和打上戳), 换代时只需要修改前半部分 */
		union{
			struct{
				hword_t age_active;
				hword_t flag;
			};
			word_t age_active_flag;
		};
		union{
			struct{
				hword_t	cid;
				apdspeff_t eff;
			};
			word_t cid_eff;
		};
		struct _cacflatnode *next;
	} CACHE_ALIGNED;
} _cacflatnode_t;

typedef struct _cacflat
{

	struct{
		lkword_t lock;
		hword_t cur_age_active;
		_cacflatnode_t *head;
	} CACHE_ALIGNED;
	_cacflatnode_t node[APDS_MAX_THREADS - 1];	/* 凑cache对齐 */

} PAGE_ALIGNED _cacflat_t;			/* 整体页对齐 */

SAI void cacflat_enq_deq(_cacflat_t *f, _cacflatnode_t *node, hword_t age_active)
{
	atm_st(&node->age_active, age_active | CACFLAT_ACTIVE);
	_cacflatnode_t *cur_head = atm_ld(&f->head);
	do
	{
		atm_st(&node->next, cur_head);
	} while (!cas_rel_s(&f->head, &cur_head, node));
}

SAI void cacflat_enq_deq_relax(_cacflat_t *f, _cacflatnode_t *node, hword_t age_active)
{
	atm_st(&node->age_active, age_active | CACFLAT_ACTIVE);
	_cacflatnode_t *cur_head = atm_ld(&f->head);
	do
	{
		atm_st(&node->next, cur_head);
	} while (!cas_s(&f->head, &cur_head, node));
}

DECL_COMBINE(cacflat)

SAI void __cacflat_init(_cacflat_t *f)
{
	f->head = (_cacflatnode_t *)NULL;
	f->lock = 0;
	f->cur_age_active = CACFLAT_ACTIVE;
}
SAI void *__cacflat_destruct(_cacflat_t *s){
	return (void *)s;
}
SAI _cacflatnode_t *__cacflat_fetch(_cacflat_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo){
	ASSERT(tinfo->tid < APDS_MAX_THREADS - 1);
	cid_t cur_cid = tinfo->cid;
	word_t cid_eff = new_word(APDS_GET_COREEFF(cur_cid), cur_cid);
	// ASSERT(cur_cid == word_low(cid_eff));
	// ASSERT(APDS_GET_COREEFF(cur_cid) == word_high(cid_eff));
	_cacflatnode_t *node = &f->node[tinfo->tid];
/* 	apds_movargs(&node->args, args);
	node->func = pfunc;
	node->cid_eff = cid_eff;
	atm_strel(&node->flag, TRUE);
	hword_t age_active = atm_ld(&node->age_active);
	if (!CACFLAT_IS_ACTIVE(age_active))
		cacflat_enq_deq(f, node, age_active); */
	hword_t age_active = atm_ld(&node->age_active);
	apds_movargs(&node->args, args);
	node->func = pfunc;
	node->cid_eff = cid_eff;


	if (!CACFLAT_IS_ACTIVE(age_active)){
		/* 减少一次memory barrier */
		atm_st(&node->age_active_flag, new_word(CACFLAT_PENDING, age_active | CACFLAT_ACTIVE));
		_cacflatnode_t *cur_head = atm_ld(&f->head);
		do
		{
			atm_st(&node->next, cur_head);
		} while (!cas_rel(&f->head, &cur_head, node));
	}
	else{
		atm_strel(&node->flag, CACFLAT_PENDING);
	}
	return node;
}

SAI apdsret_t __cacflat_wait(_cacflat_t *f, _cacflatnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy)){
	waitcnt_t cnt = -1;
	word_t cid_eff;
	word_t age_active_flag;
	hword_t age_active;
	lkword_t locked = 1;
	lkword_t unlocked = 0;
	lkword_t lock;
	lkword_t next_eff = 0;
	cid_t cur_cid = tinfo->cid;
	hword_t const help_quota = -1;

	apdspeff_t cur_eff = APDS_GET_COREEFF(cur_cid);
	lock = atm_ld(&f->lock);
	do {
		age_active_flag = atm_ld(&node->age_active_flag);
		age_active = CACFLAT_GET_AGE_ACTIVE(age_active_flag);
		while ((lock > cur_eff) && CACFLAT_GET_FLAG(age_active_flag) && CACFLAT_IS_ACTIVE(age_active))
		{
			cnt = waitpolicy->waitf(&f->lock, &locked, cnt);
			lock = atm_ld(&f->lock);
			age_active_flag = atm_ld(&node->age_active_flag);
			age_active = CACFLAT_GET_AGE_ACTIVE(age_active_flag);
		}
		/* 被其他线程完成 */
		if (!CACFLAT_GET_FLAG(age_active_flag))
		{
			mp_rmb();/*  同一cache行且存在读依赖 */
			return apds_args_ret(&node->args);
		}
			/* 只有大于等于lock效率的线程才有资格竞争 */
		if (lock <= cur_eff){
			if (cas_acq_s(&f->lock, &lock, -1))
				break;
		}
		/* 被出队? TODO: 修改后不存在PENDING还被出队的情况 */
		if (!CACFLAT_IS_ACTIVE(age_active))
			cacflat_enq_deq(f, node, age_active);
	} while (TRUE);

	/* mp_rmb(); */

	age_active_flag = atm_ld(&node->age_active_flag);
	/* 不需要内存屏障的入队，因为combiner是队列的拥有者 */
	if (!CACFLAT_IS_ACTIVE(CACFLAT_GET_AGE_ACTIVE(age_active_flag)))
		cacflat_enq_deq_relax(f, node, 0);
	/* 进入帮助流程 */
	_cacflatnode_t *phead = atm_ld(&f->head);
	hword_t cur_age_active = CACFLAT_INCR_AGE(atm_ld(&f->cur_age_active));
	/* ASSERT(CACFLAT_IS_ACTIVE(cur_age_active)); */
	word_t cur_age_active_flag = new_word(FALSE, cur_age_active);
	/* ASSERT(CACFLAT_IS_ACTIVE(CACFLAT_GET_AGE_ACTIVE(cur_age_active_flag)) && !CACFLAT_GET_FLAG(cur_age_active_flag)); */
	_cacflatnode_t *cur = node;

	_cacflatnode_t *next = phead;
	
	hword_t helpcnt = 0;
	hword_t flag = CACFLAT_GET_FLAG(age_active_flag);
	hword_t teff;
	/* 从当前开始执行 */
	/* APDS_PREFETCH(next); */
	/* 先完成自己的操作再处理其他 */
	if(LIKELY(flag)){
		apds_set_ret(&cur->args, apds_sync_single(cur->func, &cur->args));
		// atm_st(&cur->flag, FALSE);
		atm_st(&cur->age_active_flag, cur_age_active_flag);
	}
	

	do
	{
		cur = next;
		next = atm_ld(&cur->next);
		/* APDS_PREFETCH(next); */
		
		if (LIKELY(atm_ld(&cur->flag)))
		{
			cid_eff = atm_ld(&cur->cid_eff);
			teff = COMBINE_TEFF(cid_eff);
			/* 等于0的时候放弃 */
			
			if (UNLIKELY(!cac_next_helpquota(teff, COMBINE_TCID(cid_eff), &cur_eff, cur_cid, help_quota))) { 
				next_eff = (lkword_t)teff;
				/* 立即返回, 不进行任何清理操作 */
				goto RET;
			}

			/* cmb(); */
			apds_set_ret(&cur->args, apds_sync_single(cur->func, &cur->args));
			atm_strel(&cur->age_active_flag, cur_age_active_flag);
			/* 如何应用pilot技术? */
		}
		/* 跳过自身节点 */
		// if (next == node) { next = node->next; }
	} while(next);


	
	/* cmb(); */
	hword_t cur_age = CACFLAT_GET_AGE(cur_age_active);
	if (UNLIKELY(0 == (cur_age % CACFLAT_CLEANUP_FREQUENCY)))
	{
		
		
		_cacflatnode_t *prev = phead;
		next = phead->next;
		while ((cur = next))
		{

			
			// age_active = atm_ld(&cur->age_active);
			next = atm_ld(&cur->next);
			age_active_flag = atm_ld(&cur->age_active_flag);
			age_active = CACFLAT_GET_AGE_ACTIVE(age_active_flag);
			/* 清理同时统计队列最大eff, 减少大核向小核转移锁所有权的概率 */
			if (CACFLAT_GET_FLAG(age_active_flag)){
				/* 如果pending */
				teff = COMBINE_TEFF(cur->cid_eff);
				/* do not clean pending && teff >= next_eff nodes */
				if (teff >= next_eff) {
					next_eff = (lkword_t)teff;
					if (next_eff > cur_eff) break;
					prev = cur;
					continue;
				}
			}
			if ((age_active + CACFLAT_CLEANUP_OLD_THRESHOLD) <= (cur_age | CACFLAT_ACTIVE)){
				/* 只清理过期, 非等待，且效率小于自己的 */
				/* if (!CACFLAT_GET_FLAG(age_active_flag) && (COMBINE_TEFF(cur->cid_eff) < cur_eff)){ */
					/* ASSERT(FLAT_IS_ACTIVE(age_active)); */
					prev->next = next;
					/* 不需要内存屏障，因为prev一定不会被移出去，因此修改prev的next是安全的 */
#ifdef HDEF_WEAK_MEMORY_ORDER
					/* 用依赖读防止重排cur->next的读取被挪到出队之后，next不可能等于cur，因为不可能存在链表成环的情况 */
					/* 根据https://www.kernel.org/doc/Documentation/memory-barriers.txt */
					/* 只有存操作可以用读依赖的方式防止重排，读操作可能会因为推测执行无效 */
					if (LIKELY(next != cur))
#endif
						atm_st(&cur->age_active, cur_age);
					continue;
				/* } */
			}
			prev = cur;
		}
	}
	/* cmb(); */
	atm_st(&f->cur_age_active, cur_age_active);
RET:
	atm_strel(&f->lock, next_eff);
	
	waitpolicy->wakef(&f->lock, &unlocked);
	return apds_args_ret(&node->args);
}

PARAM_NONULL(1) SAI _cacflat_t *_cacflat_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))
{
	PARAM_USED(nodemgr);
	if (size < sizeof(_cacflat_t)) return NULL;
	_cacflat_t *f = (_cacflat_t *)ptr;
	__cacflat_init(f);
	return f;
}
PARAM_NONULL(1) SAI void *_cacflat_destruct(_cacflat_t *f, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	void *rptr = __cacflat_destruct(f);
	return rptr;
}
PARAM_NONULL(1, 2, 3) SAI _cacflatnode_t *_cacflat_fetch(_cacflat_t *f, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	return __cacflat_fetch(f, pfunc, args, tinfo);
}
PARAM_NONULL(1, 2) SAI apdsret_t _cacflat_wait(_cacflat_t *f, _cacflatnode_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){
	PARAM_USED(nodemgr);
	apdsret_t ret = __cacflat_wait(f, node, tinfo, waitpolicy);
	return ret;
}

